//调用此文件函数时，已确保无任何错误，无需错误检查
#include "midcode.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRANS_PARAM 0
#define TRANS_VAR 1
char *func_write = "write";
char *func_read = "read";
char *notequal = "!=";
extern InterCode ichead;
int tempvarnum = 1;
int labelnum = 1;
int offset = 0;
stack args_stack;
/*
构造函数
*/

InterCode CreateInterCodeHead()
{
    InterCode p = (InterCode)malloc(sizeof(struct InterCode_));
    p->next = NULL;
    p->prev = NULL;
    return p;
}
void CreateInterCodeNodeTwoop(int kind, twoop as, InterCode head)
{
    InterCode p = (InterCode)malloc(sizeof(struct InterCode_));
    p->next = NULL;
    p->kind = kind;
    p->u.tw = as;
    InterCode q = head;
    while (q->next != NULL)
        q = q->next;
    q->next = p;
    p->prev = q;
    return;
}
void CreateInterCodeNodeThreeop(int kind, threeop th, InterCode head)
{
    InterCode p = (InterCode)malloc(sizeof(struct InterCode_));
    p->next = NULL;
    p->kind = kind;
    p->u.th = th;
    InterCode q = head;
    while (q->next != NULL)
        q = q->next;
    q->next = p;
    p->prev = q;
    return;
}
void CreateInterCodeNodeOneop(int kind, Operand pa, InterCode head)
{
    InterCode p = (InterCode)malloc(sizeof(struct InterCode_));
    p->next = NULL;
    p->kind = kind;
    p->u.pa = pa;
    InterCode q = head;
    while (q->next != NULL)
        q = q->next;
    q->next = p;
    p->prev = q;
    return;
}

void CreateInterCodeNodeGOTO(int kind, gotoop go, InterCode head)
{
    InterCode p = (InterCode)malloc(sizeof(struct InterCode_));
    p->next = NULL;
    p->kind = kind;
    p->u.go = go;
    InterCode q = head;
    while (q->next != NULL)
        q = q->next;
    q->next = p;
    p->prev = q;
    return;
}

Operand CreateOperandVar(int kind, char *name)
{
    Operand p = (Operand)malloc(sizeof(struct Operand_));
    p->kind = kind;
    p->u.name = name;
    return p;
}
Operand CreateOperandTemp(int kind)
{
    Operand p = (Operand)malloc(sizeof(struct Operand_));
    p->kind = kind;
    p->u.var_no = tempvarnum;
    tempvarnum++;
    return p;
}
Operand CreateOperandOffset(int kind)
{
    Operand p = (Operand)malloc(sizeof(struct Operand_));
    p->kind = kind;
    p->u.var_no = tempvarnum;
    tempvarnum++;
    return p;
}
Operand CreateOperandConst(int kind, int value)
{
    Operand p = (Operand)malloc(sizeof(struct Operand_));
    p->kind = kind;
    p->u.value = value;
    return p;
}
threeop CreateThreeop(Operand result, Operand op1, Operand op2)
{
    threeop p = (threeop)malloc(sizeof(struct threeop_));
    p->result = result;
    p->op1 = op1;
    p->op2 = op2;
    return p;
}
twoop CreateTwoop(Operand result, Operand op)
{
    twoop p = (twoop)malloc(sizeof(struct twoop_));
    p->result = result;
    p->op = op;
    return p;
}
Operand newLabel()
{
    Operand p = (Operand)malloc(sizeof(struct Operand_));
    p->kind = LABEL;
    p->u.value = labelnum;
    labelnum++;
    return p;
}
gotoop CreateGotoOp(Operand op1, Operand relop, Operand op2, Operand label)
{
    gotoop p = (gotoop)malloc(sizeof(struct gotoop_));
    p->op1 = op1;
    p->op2 = op2;
    p->relop = relop;
    p->label = label;
    return p;
}

/*
输出函数
*/

char *getName(Operand op)
{
    char *p = (char *)malloc(10);
    switch (op->kind)
    {
    case VARIABLE:
        strcpy(p, op->u.name);
        return p;
    case TEMP_VARIABLE:
        sprintf(p, "t%d", op->u.var_no);
        return p;
    case CONSTANT:
        sprintf(p, "#%d", op->u.value);
        return p;
    case FUNCTION:
        strcpy(p, op->u.name);
        return p;
    case OP_RELOP:
        strcpy(p, op->u.name);
        return p;
    case LABEL:
        sprintf(p, "label%d", op->u.value);
        return p;
    case CON_FLOAT:
        sprintf(p, "#%f", op->u.value_f);
        return p;
    case ADDRESS:
        sprintf(p, "&%s", op->u.name);
        return p;
    case OFFSET:
        sprintf(p, "*t%d", op->u.var_no);
        return p;
    }
}
void outputInterCode(FILE *f)
{
    InterCode p = ichead;
    p = p->next;
    while (p != NULL)
    {
        char name1[10];
        switch (p->kind)
        {
        case IC_Label:
            fprintf(f, "LABEL %s :\n", getName(p->u.pa));
            break;
        case IC_Func:
            fprintf(f, "FUNCTION %s :\n", getName(p->u.pa));
            break;
        case IC_Assign:
            fprintf(f, "%s := %s \n", getName(p->u.tw->result), getName(p->u.tw->op));
            break;
        case IC_Add:
            fprintf(f, "%s := %s + %s \n", getName(p->u.th->result), getName(p->u.th->op1), getName(p->u.th->op2));
            break;
        case IC_Sub:
            fprintf(f, "%s := %s - %s \n", getName(p->u.th->result), getName(p->u.th->op1), getName(p->u.th->op2));
            break;
        case IC_Mul:
            fprintf(f, "%s := %s * %s \n", getName(p->u.th->result), getName(p->u.th->op1), getName(p->u.th->op2));
            break;
        case IC_Div:
            fprintf(f, "%s := %s / %s \n", getName(p->u.th->result), getName(p->u.th->op1), getName(p->u.th->op2));
            break;
        case IC_Goto:
            fprintf(f, "GOTO %s \n", getName(p->u.pa));
            break;
        case IC_If:
            fprintf(f, "IF %s %s %s GOTO %s \n", getName(p->u.go->op1), getName(p->u.go->relop), getName(p->u.go->op2), getName(p->u.go->label));
            break;
        case IC_Retrun:
            fprintf(f, "RETURN %s \n", getName(p->u.pa));
            break;
        case IC_Dec:
            fprintf(f, "DEC %s %d\n", getName(p->u.tw->result), p->u.tw->op->u.value);
            break;
        case IC_Arg:
            fprintf(f, "ARG %s \n", getName(p->u.pa));
            break;
        case IC_Call:
            fprintf(f, "%s := CALL %s \n", getName(p->u.tw->result), getName(p->u.tw->op));
            break;
        case IC_Param:
            fprintf(f, "PARAM %s \n", getName(p->u.pa));
            break;
        case IC_Read:
            fprintf(f, "READ %s \n", getName(p->u.pa));
            break;
        case IC_Write:
            fprintf(f, "WRITE %s \n", getName(p->u.pa));
            break;
        }
        p = p->next;
    }
}

/*
分析函数
*/

void trans_Program(NODE *node)
{
    ichead = CreateInterCodeHead();
    stackHead();
    trans_ExtDeflist(node->child[0]);
}

void trans_ExtDeflist(NODE *node)
{
    if (node == NULL)
        return;
    trans_ExtDef(node->child[0]);
    trans_ExtDeflist(node->child[1]);
}

void trans_ExtDef(NODE *node)
{
    /*
    由于没有结构体和全局变量，因此只需要分析Specifier FunDec CompSt产生式
    */
    if (node->num == 2 || node->child[2]->type == _SEMI)
        return;
    trans_FunDec(node->child[1]);
    trans_Compst(node->child[2]);
}

void trans_FunDec(NODE *node)
{
    char *name = TID(node->child[0]);
    CreateInterCodeNodeOneop(IC_Func, CreateOperandVar(FUNCTION, name), ichead);
    if (node->num == 4)
        trans_VarList(node->child[2]);
}
void trans_VarList(NODE *node)
{
    if (node->num == 1)
        trans_ParamDec(node->child[0]);
    else
    {
        trans_ParamDec(node->child[0]);
        trans_VarList(node->child[2]);
    }
}
void trans_ParamDec(NODE *node)
{
    trans_VarDec(node->child[1], TRANS_PARAM);
}
Operand trans_VarDec(NODE *node, int trans_kind)
{
    if (node->num == 1)
    {
        Symbol var = lookup(TID(node->child[0]));
        Operand p = CreateOperandVar(VARIABLE, var->name);
        if (trans_kind == TRANS_PARAM)
            CreateInterCodeNodeOneop(IC_Param, CreateOperandVar(VARIABLE, var->name), ichead);
        else if (var->kind == VAR && var->u.var->kind == ARRAY)
        {
            int range = 1;
            Array ay = var->u.var->u.array;
            while (ay->elem->kind != BASIC)
            {
                range = range * ay->size;
                ay = ay->elem->u.array;
            }
            range = range * ay->size;
            range = (ay->elem->u.basic == TYPE_INT) ? 4 * range : 8 * range;
            CreateInterCodeNodeTwoop(IC_Dec, CreateTwoop(p, CreateOperandConst(CONSTANT, range)), ichead);
        }
        return p;
    }
    return trans_VarDec(node->child[0], trans_kind);
}

void trans_Compst(NODE *node)
{
    trans_DefList(node->child[1]);
    trans_StmtList(node->child[2]);
}

void trans_DefList(NODE *node)
{
    if (node == NULL)
        return;
    trans_Def(node->child[0]);
    trans_DefList(node->child[1]);
}
void trans_Def(NODE *node)
{
    trans_DecList(node->child[1]);
}
void trans_DecList(NODE *node)
{
    if (node->num == 1)
        trans_Dec(node->child[0]);
    else
    {
        trans_Dec(node->child[0]);
        trans_DecList(node->child[2]);
    }
}
void trans_Dec(NODE *node)
{
    Operand p, q;
    if (node->num == 1)
        trans_VarDec(node->child[0], TRANS_VAR);
    else
    {
        p = trans_VarDec(node->child[0], TRANS_VAR);
        q = trans_Exp(node->child[2]);
        CreateInterCodeNodeTwoop(IC_Assign, CreateTwoop(p, q), ichead);
    }
}

void trans_StmtList(NODE *node)
{
    if (node == NULL)
        return;
    trans_Stmt(node->child[0]);
    trans_StmtList(node->child[1]);
}

void trans_Stmt(NODE *node)
{
    Operand p, q, k, n;
    switch (node->child[0]->type)
    {
    case _Exp:
        trans_Exp(node->child[0]);
        return;
    case _RETURN:
        p = trans_Exp(node->child[1]);
        CreateInterCodeNodeOneop(IC_Retrun, p, ichead);
        return;
    case _WHILE:
        p = newLabel();
        q = newLabel();
        n = newLabel();
        CreateInterCodeNodeOneop(IC_Label, p, ichead);
        trans_Cond(node->child[2], q, n);
        CreateInterCodeNodeOneop(IC_Label, q, ichead);
        trans_Stmt(node->child[4]);
        CreateInterCodeNodeOneop(IC_Goto, p, ichead);
        CreateInterCodeNodeOneop(IC_Label, n, ichead);
        return;
    case _IF:
        p = newLabel();
        q = newLabel();
        k = trans_Cond(node->child[2], p, q);
        if (node->num == 5)
        {
            CreateInterCodeNodeOneop(IC_Label, p, ichead);
            trans_Stmt(node->child[4]);
            CreateInterCodeNodeOneop(IC_Label, q, ichead);
        }
        else if (node->num == 7)
        {
            n = newLabel();
            CreateInterCodeNodeOneop(IC_Label, p, ichead);
            trans_Stmt(node->child[4]);
            CreateInterCodeNodeOneop(IC_Goto, n, ichead);
            CreateInterCodeNodeOneop(IC_Label, q, ichead);
            trans_Stmt(node->child[6]);
            CreateInterCodeNodeOneop(IC_Label, n, ichead);
        }
        return;
    case _CompSt:
        trans_Compst(node->child[0]);
        return;
    }
}

Operand trans_Cond(NODE *node, Operand label_true, Operand label_false)
{
    Operand p, q, k, label1;
    if (node->num == 2 && node->child[0]->type == _NOT)
    {
        return trans_Cond(node->child[1], label_false, label_true);
    }
    else if (node->num == 3)
    {
        if (node->child[1]->type == _RELOP)
        {
            p = trans_Exp(node->child[0]);
            q = trans_Exp(node->child[2]);
            k = CreateOperandVar(OP_RELOP, TID(node->child[1]));
            CreateInterCodeNodeGOTO(IC_If, CreateGotoOp(p, k, q, label_true), ichead);
            CreateInterCodeNodeOneop(IC_Goto, label_false, ichead);
            return p;
        }
        else if (node->child[1]->type == _AND)
        {
            label1 = newLabel();
            trans_Cond(node->child[0], label1, label_false);
            CreateInterCodeNodeOneop(IC_Label, label1, ichead);
            trans_Cond(node->child[2], label_true, label_false);
            return p;
        }
        else if (node->child[1]->type == _OR)
        {
            label1 = newLabel();
            trans_Cond(node->child[0], label_true, label1);
            CreateInterCodeNodeOneop(IC_Label, label1, ichead);
            trans_Cond(node->child[2], label_true, label_false);
            return p;
        }
    }
    p = trans_Exp(node);
    k = CreateOperandVar(OP_RELOP, notequal);
    q = CreateOperandConst(CONSTANT, 0);
    CreateInterCodeNodeGOTO(IC_If, CreateGotoOp(p, k, q, label_true), ichead);
    CreateInterCodeNodeOneop(IC_Goto, label_false, ichead);
    return p;
}

Operand trans_Exp(NODE *node)
{
    switch (node->num)
    {
    case 1:
        return trans_ExpOneNode(node);
    case 2:
        return trans_ExpTwoNode(node);
    case 3:
        return trans_ExpThreeNode(node);
    case 4:
        return trans_ExpFourNode(node);
    }
}

Operand trans_ExpOneNode(NODE *node)
{
    char *name;
    Operand p;
    if (node->child[0]->type == _INT)
        return CreateOperandConst(CONSTANT, TINT(node->child[0]));
    else if (node->child[0]->type == _FLOAT)
        return CreateOperandConst(CON_FLOAT, TFLOAT(node->child[0]));
    else
        return CreateOperandVar(VARIABLE, TID(node->child[0]));
}

Operand trans_ExpTwoNode(NODE *node)
{
    Operand p, q, k;
    // not 放在trans_Cond中处理
    p = CreateOperandTemp(TEMP_VARIABLE);
    q = trans_Exp(node->child[1]);
    k = CreateOperandConst(CONSTANT, 0);
    // p = #0 - q
    CreateInterCodeNodeThreeop(IC_Sub, CreateThreeop(p, k, q), ichead);
    return p;
}

Operand trans_ExpThreeNode(NODE *node)
{
    Operand p, q, k;
    char *name;
    Symbol s;
    switch (node->child[1]->type)
    {
    case _ASSIGNOP:
        p = trans_Exp(node->child[0]);
        q = trans_Exp(node->child[2]);
        CreateInterCodeNodeTwoop(IC_Assign, CreateTwoop(p, q), ichead);
        return p;
    //交给trans_Cond处理，此处不会遇到
    case _AND:
    case _OR:
    case _RELOP:
        return NULL;
    case _PLUS:
        p = CreateOperandTemp(TEMP_VARIABLE);
        q = trans_Exp(node->child[0]);
        k = trans_Exp(node->child[2]);
        CreateInterCodeNodeThreeop(IC_Add, CreateThreeop(p, q, k), ichead);
        return p;
    case _MINUS:
        p = CreateOperandTemp(TEMP_VARIABLE);
        q = trans_Exp(node->child[0]);
        k = trans_Exp(node->child[2]);
        CreateInterCodeNodeThreeop(IC_Sub, CreateThreeop(p, q, k), ichead);
        return p;
    case _STAR:
        p = CreateOperandTemp(TEMP_VARIABLE);
        q = trans_Exp(node->child[0]);
        k = trans_Exp(node->child[2]);
        CreateInterCodeNodeThreeop(IC_Mul, CreateThreeop(p, q, k), ichead);
        return p;
    case _DIV:
        p = CreateOperandTemp(TEMP_VARIABLE);
        q = trans_Exp(node->child[0]);
        k = trans_Exp(node->child[2]);
        CreateInterCodeNodeThreeop(IC_Div, CreateThreeop(p, q, k), ichead);
        return p;
    case _Exp:
        return trans_Exp(node->child[1]);
    case _LP:
        name = TID(node->child[0]);
        s = lookup(name);
        if (!strcmp(name, "read"))
        {
            p = CreateOperandTemp(TEMP_VARIABLE);
            CreateInterCodeNodeOneop(IC_Read, p, ichead);
            return p;
        }
        CreateInterCodeNodeTwoop(IC_Call, CreateTwoop(CreateOperandTemp(TEMP_VARIABLE), CreateOperandVar(FUNCTION, name)), ichead);
        return p;
    default: //结构体暂时不处理
        return p;
    }
}

Operand trans_ExpFourNode(NODE *node)
{
    Operand p, q, k;

    Symbol s;
    //含参函数
    if (node->child[0]->type == _ID)
    {
        stack temp = args_stack, cur;
        char *name = TID(node->child[0]);
        p = trans_Args(node->child[2]);
        if (!strcmp(name, "write"))
        {

            CreateInterCodeNodeOneop(IC_Write, p, ichead);
            while (args_stack->next != NULL)
            {
                cur = args_stack->next;
                args_stack->next = cur->next;
                free(cur);
            }
            return p;
        }
        while (args_stack->next != NULL)
        {
            cur = args_stack->next;
            CreateInterCodeNodeOneop(IC_Arg, cur->node, ichead);
            args_stack->next = cur->next;
            free(cur);
        }
        k = CreateOperandTemp(TEMP_VARIABLE);
        CreateInterCodeNodeTwoop(IC_Call, CreateTwoop(k, CreateOperandVar(FUNCTION, name)), ichead);
    }
    // 只存在一维数组
    else
    {
        p = trans_Exp(node->child[0]);
        p->kind = ADDRESS;
        q = trans_Exp(node->child[2]);
        Symbol var = lookup(TID(node->child[0]->child[0]));
        Array ar = var->u.var->u.array;
        int size = ar->elem->u.basic == TYPE_INT ? 4 : 8;
        // q*size + &p
        k = CreateOperandTemp(TEMP_VARIABLE);
        CreateInterCodeNodeThreeop(IC_Mul, CreateThreeop(k, q, CreateOperandConst(CONSTANT, size)), ichead);
        q = CreateOperandTemp(TEMP_VARIABLE);
        CreateInterCodeNodeThreeop(IC_Add, CreateThreeop(q, p, k), ichead);
        tempvarnum--;
        k = CreateOperandOffset(OFFSET);
    }
    return k;
}

Operand trans_Args(NODE *node)
{
    Operand q;
    q = trans_Exp(node->child[0]);
    insert(q);
    if (node->num != 1)
        trans_Args(node->child[2]);
    return q;
}

void stackHead()
{
    args_stack = (stack)malloc(sizeof(struct stack_));
    args_stack->node = NULL;
    args_stack->next = NULL;
}

void insert(Operand op)
{
    stack q = args_stack;
    stack p = (stack)malloc(sizeof(struct stack_));
    p->next = q->next;
    q->next = p;
    p->node = op;
}